package com.xiaoq.oauth2.endpoint;

import java.io.IOException;
import java.util.ArrayList;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xiaoq.coms.FrameworkErrorCode;
import com.xiaoq.coms.RestException;
import com.xiaoq.oauth2.OAuth2Constants;
import com.xiaoq.oauth2.entity.OAuth2TokenEntity;
import com.xiaoq.oauth2.service.OAuth2Service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * @author xinpeng created on 16/5/31-下午3:49.
 *
 *         授权码模式（authorization code）
 */

@RestController
@RequestMapping("/api/v1/oauth2")
@Api(tags={"Token相关Api"})
class ACTokenEndPoint {

	private static Logger logger = LoggerFactory.getLogger(ACTokenEndPoint.class);

	private static final String signingKey = "lifestyle365-secret-201611120-20191119";
	
	private static final Pattern REDIRECT_PATTERN = Pattern.compile("^[a-zA-Z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\s*)?$");

	private Cache cache;

	@Autowired
	public ACTokenEndPoint(CacheManager cacheManager) {
		this.cache = cacheManager.getCache("oauth2-cache");
	}

	@Autowired
	private OAuth2Service oAuth2Service;

	/**
	 * 认证服务器申请令牌(AccessToken) [验证client_id、client_secret、auth code的正确性或更新令牌
	 * refresh_token]
	 *
	 * @url http://{url}:{port}/oauth2/access_token?client_id={AppKey}&
	 *      client_secret={AppSecret}&grant_type=authorization_code&redirect_uri={YourSiteUrl}&code={code}
	 */
	@RequestMapping(value = "/access_token", method = RequestMethod.POST)
	public ResponseEntity<?> access_token(HttpServletRequest request)
			throws IOException, OAuthSystemException, java.net.URISyntaxException {

		logger.info("starting to generate access_token............");

		try {
			// 构建oauth2请求
			OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);

			// 1、验证redirecturl格式是否合法 (8080端口测试)
			if (!oauthRequest.getRedirectURI().contains(":8080")
					&& !REDIRECT_PATTERN.matcher(oauthRequest.getRedirectURI()).matches()) {
				throw new RestException(HttpStatus.BAD_REQUEST, OAuthError.CodeResponse.INVALID_REQUEST);
			}
			// 2、验证Clientkey是否正确
			if (!validateOAuth2ClientKey(oauthRequest)) {
				throw new RestException(HttpStatus.BAD_REQUEST, OAuthError.CodeResponse.UNAUTHORIZED_CLIENT);
			}
			// 3、验证客户端安全ClientSecret是否正确
			if (!validateOAuth2ClientSecret(oauthRequest)) {
				throw new RestException(HttpStatus.BAD_REQUEST, OAuth2Constants.INVALID_CLIENT_SECRET);
			}
			String authzCode = oauthRequest.getCode();

			logger.info("authzCode = " + authzCode);

			// 4、验证AUTHORIZATION_CODE
			if (GrantType.AUTHORIZATION_CODE.name().equalsIgnoreCase(oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE))) {
				if (cache.get(authzCode) == null) {
					throw new RestException(HttpStatus.BAD_REQUEST, OAuth2Constants.INVALID_AUTH_CODE);
				}
			}

			// 5、构建oauth2授权返回信息
			OAuth2TokenEntity oAuth2_tokenEntity = oAuth2Service.getAccessTokenAndRereshToken4ThirdAPP(authzCode,
					oauthRequest.getUsername(), oauthRequest.getClientSecret(), 30);

			String bearerAccessToken = "Bearer " + oAuth2_tokenEntity.getAccessToken();
			String refreshToken = oAuth2_tokenEntity.getRefreshToken();

			HttpHeaders httpHeaders = new HttpHeaders();
			httpHeaders.set("access_token", bearerAccessToken);
			httpHeaders.set("refresh_token", refreshToken);

			logger.info("bearer access_token : " + bearerAccessToken);
			logger.info("refresh_token : " + refreshToken);

			return new ResponseEntity<>(httpHeaders, HttpStatus.OK);

		} catch (OAuthProblemException ex) {
			throw new RestException(HttpStatus.BAD_REQUEST, OAuth2Constants.INVALID_AUTH_CODE);
		}

	}

	/**
	 * 刷新令牌
	 *
	 * @throws IOException
	 * @throws OAuthSystemException
	 * @url http://{url}:{port}/oauth2/{loginAccount}/refresh_token?
	 *      client_id={client_id}&client_secret={client_secret}&grant_type=refresh_token&refresh_token={refresh_token}
	 */
	@ApiOperation(value = "刷新Token", notes = "刷新Token")
	@RequestMapping(value = "/refresh_token", method = RequestMethod.POST)
	public Object refresh_token(@RequestParam String client_id, @RequestParam String client_secret,
			@RequestParam String grant_type, @RequestParam String refresh_token)
			throws IOException, OAuthSystemException {

		logger.debug("client_id = " + client_id);
		logger.debug("client_secret = " + client_secret);
		logger.debug("grant_type = " + grant_type);
		logger.debug("refresh_token = " + refresh_token);

		// 1、验证Clientkey是否正确
		if (!validateOAuth2ClientKey(client_id)) {
			throw new RestException(HttpStatus.UNAUTHORIZED, OAuthError.CodeResponse.UNAUTHORIZED_CLIENT);
		}
		// 2、验证ClientSecret是否正确
		if (!validateOAuth2ClientSecret(client_secret)) {
			throw new RestException(HttpStatus.UNAUTHORIZED, OAuth2Constants.INVALID_CLIENT_SECRET);
		}
		// 3、验证grant_type是否为refresh_token
		if (!GrantType.REFRESH_TOKEN.name().equalsIgnoreCase(grant_type)) {
			throw new RestException(HttpStatus.UNAUTHORIZED, OAuthError.TokenResponse.UNSUPPORTED_GRANT_TYPE);
		}

		// 4、验证refresh_token是否超时以及其它合法有效性验证
		String userId;
		try {

			final Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(refresh_token).getBody();

			userId = claims.get("userId").toString();

			logger.info("The userId parser from refresh_token is : " + userId);

		} catch (ExpiredJwtException expEx) {// 刷新令牌过期,返回9903,客户端必须重新登录
			expEx.printStackTrace();
			throw new RestException(HttpStatus.FORBIDDEN,
					FrameworkErrorCode.REFRESH_TOKEN_EXPIRED.errorMsg + ": " + expEx.getMessage(),
					FrameworkErrorCode.REFRESH_TOKEN_EXPIRED.value());
		} catch (final Exception e) { // 刷新令牌无效,返回9904,客户端必须重新登录

			// todo: 非法访问考虑加入黑名单
			e.printStackTrace();
			throw new RestException(HttpStatus.FORBIDDEN,
					FrameworkErrorCode.INVALID_REFRESH_TOKEN.errorMsg + ": " + e.getMessage(),
					FrameworkErrorCode.INVALID_REFRESH_TOKEN.value());
		}

		if (userId == null || "".equals(userId)) {
			throw new RestException(HttpStatus.FORBIDDEN, 
					FrameworkErrorCode.INVALID_REFRESH_TOKEN.errorMsg,
					FrameworkErrorCode.INVALID_REFRESH_TOKEN.value());
		}	
		// 5、如果refresh_token合法有效,则重新生成一个新的access_token,refresh_token不变
		OAuth2TokenEntity oAuth2_tokenEntity = oAuth2Service.getAccessTokenAndRefreshToken4XiaoqAPP(userId,
				signingKey, 30);
		String new_bearerAccessToken = "Bearer " + oAuth2_tokenEntity.getAccessToken();
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.set("access_token", new_bearerAccessToken);
		httpHeaders.set("refresh_token", refresh_token);
		return new ResponseEntity<>(httpHeaders, HttpStatus.OK);

	}

	/**
	 * 验证ClientID 是否正确
	 *
	 */
	private boolean validateOAuth2ClientKey(OAuthTokenRequest oauthRequest) {
		// 客户端Clientkey
		ArrayList<String> arrayKeys = new ArrayList<>();
		arrayKeys.add("c1ebe466-1cdc-4bd3-ab69-77c3561b9dee");
		arrayKeys.add("2943fea6-4acc-4266-a820-844e1c122c51");
		arrayKeys.add("f41650a6-dd03-433f-83ff-057c3b783ef0");

		return arrayKeys.contains(oauthRequest.getClientId());
	}

	/**
	 * 验证ClientID 是否正确
	 *
	 */
	private boolean validateOAuth2ClientKey(String clientId) {
		// 客户端Clientkey
		ArrayList<String> arrayKeys = new ArrayList<>();
		arrayKeys.add("c1ebe466-1cdc-4bd3-ab69-77c3561b9dee");
		arrayKeys.add("2943fea6-4acc-4266-a820-844e1c122c51");
		arrayKeys.add("f41650a6-dd03-433f-83ff-057c3b783ef0");
		arrayKeys.add("lifestyle365-client-key-app");

		return arrayKeys.contains(clientId);
	}

	/**
	 * 验证AppSecret 是否正确
	 *
	 */
	private boolean validateOAuth2ClientSecret(OAuthTokenRequest oauthRequest) {
		// 客户端ClientSecret
		ArrayList<String> arrayKeys = new ArrayList<>();
		arrayKeys.add("d8346ea2-6017-43ed-ad68-19c0f971738b");
		arrayKeys.add("b6a06e79-9ebe-4541-968d-91d7476e2e64");
		arrayKeys.add("764b6a3b-ec54-41c2-97b0-6d42e0c54a24");
		arrayKeys.add("lifestyle365-secret-201611120-20191119");
		return arrayKeys.contains(oauthRequest.getClientSecret());
	}

	/**
	 * 验证AppSecret 是否正确
	 *
	 */
	private boolean validateOAuth2ClientSecret(String clientSecret) {
		// 客户端ClientSecret
		ArrayList<String> arrayKeys = new ArrayList<>();
		arrayKeys.add("d8346ea2-6017-43ed-ad68-19c0f971738b");
		arrayKeys.add("b6a06e79-9ebe-4541-968d-91d7476e2e64");
		arrayKeys.add("764b6a3b-ec54-41c2-97b0-6d42e0c54a24");
		arrayKeys.add("lifestyle365-secret-201611120-20191119");
		arrayKeys.add("lifestyle365-client-secret-app");
		return arrayKeys.contains(clientSecret);
	}

}
